//
// Created by 陈成 on 2020/2/11.
//

#include "InstDecoder.h"

InstDecoder::InstDecoder(sc_module_name name) : sc_module(name) {
    target_socket.register_b_transport(this, &InstDecoder::recieveInst);

    SC_THREAD(sendInst);

    r = new RegBank<REG_NUM>();
    csr = new RegBank<CSR_NUM>();
    m = new Mem();

    pc = 0;
}

void
InstDecoder::recieveInst(tlm::tlm_generic_payload &payload, sc_core::sc_time &delay_time) {
    std::cout << "InstDecoder got the data" << std::endl;

    sc_bv<32> inst = (
            sc_bv<8>(*(payload.get_data_ptr() + 3)),
            sc_bv<8>(*(payload.get_data_ptr() + 2)),
            sc_bv<8>(*(payload.get_data_ptr() + 1)),
            sc_bv<8>(*(payload.get_data_ptr() + 0))
            );

    Inst *i;

    sc_bv<7> opcode = inst.range(6, 0);
    switch (opcode.to_uint()) {
        case 0b0000011:
        case 0b0000111:
        case 0b0011011:
        case 0b0100011:
        case 0b0101011:
        case 0b0101111:
        case 0b0110011:
            i = new InstI(pc, inst, r, csr, m);
            break;
        case 0b0001011:
        case 0b0001111:
            i = new InstU(pc, inst, r, csr, m);
            break;
        case 0b0010011:
            i = new InstR(pc, inst, r, csr, m);
            break;
        case 0b0010111:
            i = new InstJ(pc, inst, r, csr, m);
            break;
        case 0b0011111:
            i = new InstB(pc, inst, r, csr, m);
            break;
        case 0b0100111:
            i = new InstS(pc, inst, r, csr, m);
            break;
        default:
            assert(false);
            break;
    }

    pc += 1;

    inst_fifo.push(i);

    payload.set_response_status(tlm::TLM_OK_RESPONSE);

    return;
}

void
InstDecoder::sendInst() {
    while (true) {
        if (inst_fifo.empty()) {
            wait(SC_ZERO_TIME);
            continue;
        }

        Inst *i = inst_fifo.front();

        tlm::tlm_generic_payload *transaction_ptr = new tlm::tlm_generic_payload();
        transaction_ptr->set_data_ptr((unsigned char *)i);
        sc_time delay = SC_ZERO_TIME;

        initiator_socket->b_transport(*transaction_ptr, delay);

        gp_status = transaction_ptr->get_response_status();

        if (gp_status == tlm::TLM_OK_RESPONSE) {
            std::cout << "InstDecoder send data success" << std::endl;
            inst_fifo.pop();

            uint32_t *new_pc = reinterpret_cast<uint32_t *>(transaction_ptr->get_data_ptr());
            std::cout << "InstDecoder got new PC " << *new_pc << std::endl;

            wait(SC_ZERO_TIME);
        } else {
            std::cout << "TLM_OK_RESPONSE_NOT" << std::endl;
        }
    }
}

InstDecoder::~InstDecoder() {
    if (r) delete r;
    if (csr) delete csr;
    if (m) delete m;
}
